﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

public class MessageItemControl
{
    private MessageItemControl() { }
    private static MessageItemControl mInstance;
    public static MessageItemControl Instance
    {
        get
        {
            if (mInstance == null)
            {
                mInstance = new MessageItemControl();
            }
            return mInstance;
        }
    }

    public List<MessageItem> Items = new List<MessageItem>();
    public int FocusID = 0;

    private bool mInitDone = false;
    private ScrollRectCopy mScroll;
    private float mTotalLength = 0;
    private float mScrollHeight = 0;
    private int mCacheDataCount = 0;
    private int mNewDataCount = 0;

    private object obj = new object();

    public void SetScroll(ScrollRectCopy scroll)
    {
        mScroll = scroll;
    }

    public void InitItems()
    {
        if (mScroll == null) return;

        InitVariables();

        var datas = MessageDataControl.Instance.Datas;
        mCacheDataCount = datas.Count;
        if (mCacheDataCount == 0) return;

        var data = datas[mCacheDataCount - 1];

        mTotalLength = data.PosY + data.Length;
        //Debug.LogError(mTotalLength);
        mScrollHeight = mScroll.Rect.rect.height;

        mScroll.content.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, mTotalLength);
        mScroll.verticalNormalizedPosition = 0;

        int maxItemCount = Mathf.RoundToInt(mScroll.Rect.rect.height / MessageDataControl.Instance.MinSize) + 1;
        if (maxItemCount > datas.Count) maxItemCount = datas.Count;

        Transform parent = mScroll.content.transform;
        for (int i = 0; i < maxItemCount; i++)
        {
            GameObject go = Tools.InstantiatePrefab("MessageItem", parent, new Vector2(0, datas[i].PosY));
            if (go == null) return;
            go.transform.SetAsFirstSibling();
            MessageItem item = go.AddComponent<MessageItem>();
            if (item == null) return;
            item.Refresh(datas[i]);
            Items.Add(item);
        }
        mInitDone = true;
    }

    private void InitVariables()
    {
        mTotalLength = 0;
        mScrollHeight = 0;
        mCacheDataCount = 0;
        mNewDataCount = 0;
        mInitDone = false;
        FocusID = 0;
    }

    public void ReInitItems()
    {
        if (mScroll == null) return;

        var datas = MessageDataControl.Instance.Datas;
        mNewDataCount = datas.Count - mCacheDataCount;
        mCacheDataCount = datas.Count;
        if (mCacheDataCount == 0) return;

        var data = datas[mCacheDataCount - 1];

        mTotalLength = data.PosY + data.Length;
        //        Debug.LogError(mTotalLength);
        mScrollHeight = mScroll.Rect.rect.height;
        mScroll.content.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, mTotalLength);
        mScroll.DOKill();

        if (mCacheDataCount <= 100)
        {
            if (mScroll.verticalNormalizedPosition <= 0.01f)
            {
                mScroll.verticalNormalizedPosition = 0.05f;
            }
            mScroll.DOVerticalNormalizedPos(0, Mathf.Max(0.2f, mScroll.verticalNormalizedPosition * 3));
        }
        else
        {
            if (mScroll.verticalNormalizedPosition <= 0.01f)
            {
                mScroll.verticalNormalizedPosition = 0.05f;
                mScroll.DOVerticalNormalizedPos(0, 0.2f);
            }
            else
            {
                if (mCacheDataCount * mScroll.verticalNormalizedPosition <= 100)
                {
                    mScroll.DOVerticalNormalizedPos(0, Mathf.Max(0.2f, mScroll.verticalNormalizedPosition * 3 * (mCacheDataCount / 100)));
                }
                else
                {
                    for (int i = 0; i < Items.Count; i++)
                    {
                        UnityEngine.GameObject.Destroy(Items[i].gameObject);
                    }
                    Items.Clear();
                    FocusID = 0;
                    mScroll.verticalNormalizedPosition = 0;
                }
            }
        }

        int maxItemCount = Mathf.RoundToInt(mScroll.Rect.rect.height / MessageDataControl.Instance.MinSize) + 1;
        if (maxItemCount > datas.Count) maxItemCount = datas.Count;

        Transform parent = mScroll.content.transform;
        if (Items.Count > 0)
        {
            if (maxItemCount > Items.Count)
            {
                int firstID = Items[0].Data.ID;

                int index = 1;
                for (int i = Items.Count; i < maxItemCount; i++)
                {
                    GameObject go = Tools.InstantiatePrefab("MessageItem", parent, new Vector2(0, datas[i].PosY));
                    if (go == null) return;
                    go.transform.SetAsFirstSibling();
                    MessageItem item = go.AddComponent<MessageItem>();
                    if (item == null) return;
                    item.Refresh(datas[firstID - index]);
                    Items.Insert(0, item);
                    index++;
                }
            }

            for (int i = 0; i < Items.Count; i++)
            {
                Items[i].Refresh();
            }
            FocusID += mNewDataCount;
        }
        else
        {
            for (int i = 0; i < maxItemCount; i++)
            {
                GameObject go = Tools.InstantiatePrefab("MessageItem", parent, new Vector2(0, datas[i].PosY));
                if (go == null) return;
                go.transform.SetAsFirstSibling();
                MessageItem item = go.AddComponent<MessageItem>();
                if (item == null) return;
                item.Refresh(datas[i]);
                Items.Add(item);
            }
        }

        mInitDone = true;
        mNewDataCount = 0;
    }

    public void Refresh(float curY, bool isUp)
    {
        //Debug.LogError("curY:" + curY + "    isUp:" + isUp);
        if (!mInitDone) return;

        var datas = MessageDataControl.Instance.Datas;
        var value = mScroll.content.rect.height - mScrollHeight;
        if (-curY > value) return;

        if (curY > 0)
        {
            if (FocusID == 0) return;

            int targetIndex = -1;
            for (int i = Items.Count - 1; i >= 0; i--)
            {
                if (Items[i].Data.ID == FocusID)
                {
                    targetIndex = i;
                    break;
                }
            }
            if (targetIndex < 0) return;

            var temp = Items.GetRange(targetIndex, Items.Count - targetIndex);
            Items.InsertRange(0, temp);
            Items.RemoveRange(Items.Count - temp.Count, temp.Count);

            var cacheItem = Items[Items.Count - 1];
            Items.Insert(0, cacheItem);
            Items.RemoveAt(Items.Count - 1);

            FocusID--;
            Items[0].Refresh(datas[FocusID]);
        }

        if (isUp)
        {
            if (FocusID == 0) return;

            if (-curY < datas[FocusID].PosY)
            {
                int targetIndex = -1;
                for (int i = Items.Count - 1; i >= 0; i--)
                {
                    if (Items[i].Data.ID == FocusID)
                    {
                        targetIndex = i;
                        break;
                    }
                }
                if (targetIndex < 0) return;

                var temp = Items.GetRange(targetIndex, Items.Count - targetIndex);
                Items.InsertRange(0, temp);
                Items.RemoveRange(Items.Count - temp.Count, temp.Count);

                var cacheItem = Items[Items.Count - 1];
                Items.Insert(0, cacheItem);
                Items.RemoveAt(Items.Count - 1);

                FocusID--;
                Items[0].Refresh(datas[FocusID]);
            }
        }
        else
        {
            if (-curY >= Items[1].Data.PosY)
            {
                var cacheItem = Items[0];
                Items.Add(cacheItem);
                Items.RemoveAt(0);

                int index = FocusID + Items.Count;
                if (index >= datas.Count) return;

                Items[Items.Count - 1].Refresh(datas[index]);
                FocusID++;
            }
        }
    }
}

public class MessageItem : MonoBehaviour
{
    public RectTransform Rect;
    public MessageData Data;
    private InlineText mText;
    private Image mHead;

    private void Awake()
    {
        Rect = GetComponent<RectTransform>();

        mText = transform.Find("Text").GetComponent<InlineText>();
        mHead = transform.Find("Head").GetComponent<Image>();
    }

    internal void Refresh(MessageData messageData)
    {
        Rect.anchoredPosition = new Vector2(0, messageData.PosY);
        Rect.sizeDelta = new Vector2(Rect.sizeDelta.x, messageData.Length);
        gameObject.name = messageData.ID.ToString();

        if (Data == messageData) return;

        Data = messageData;

        RefreshText();
        RefreshHead();
    }

    internal void Refresh()
    {
        Rect.anchoredPosition = new Vector2(0, Data.PosY);
        Rect.sizeDelta = new Vector2(Rect.sizeDelta.x, Data.Length);
        gameObject.name = Data.ID.ToString();
        RefreshText();
        RefreshHead();
    }

    private void RefreshText()
    {
        if (mText.text == Data.Text)
        {
            mText.enabled = false;
            mText.enabled = true;
        }
        else
        {
            mText.text = Data.Text;
        }
        mText.rectTransform.sizeDelta = new Vector2(mText.rectTransform.sizeDelta.x, Data.Length);

        //mText.alignment = Data.IsSelf ? TextAnchor.UpperRight : TextAnchor.UpperLeft;
    }

    private void RefreshHead()
    {
        if (Data.IsSelf)
        {
            mHead.rectTransform.anchorMax = Vector2.one;
            mHead.rectTransform.anchorMin = Vector2.one;

            mHead.rectTransform.anchoredPosition = Vector2.one * -40;
        }
        else
        {
            mHead.rectTransform.anchorMax = Vector2.up;
            mHead.rectTransform.anchorMin = Vector2.up;

            mHead.rectTransform.anchoredPosition = new Vector2(40, -40);
        }
    }
}

public class MessageDataControl
{
    private MessageDataControl() { }
    private static MessageDataControl mInstance;
    public static MessageDataControl Instance
    {
        get
        {
            if (mInstance == null)
            {
                mInstance = new MessageDataControl();
            }
            return mInstance;
        }
    }

    public const int C_Spacing = 10;
    public int MinSize { get; private set; }
    public List<MessageData> Datas = new List<MessageData>();

    public void Clear() { Datas.Clear(); }

    public void InitDatas(List<MessageBaseData> datas)
    {
        if (datas == null) return;
        if (datas.Count == 0) return;

        Clear();

        MinSize = datas[0].Length;
        Datas.Add(new MessageData(0, datas[0], 0));
        for (int i = 1; i < datas.Count; i++)
        {
            Datas.Add(new MessageData(i, datas[i], Datas[i - 1].PosY + Datas[i - 1].Length + C_Spacing));
            if (MinSize > datas[i].Length) MinSize = datas[i].Length;
        }
    }

    public void AddDataAtTop(MessageBaseData data)
    {
        if (Datas.Count == 0) MinSize = data.Length;
        else if (MinSize > data.Length) MinSize = data.Length;

        Datas.Insert(0, new MessageData(0, data, 0));
        for (int i = 1; i < Datas.Count; i++)
        {
            Datas[i].PosY = Datas[i - 1].PosY + Datas[i - 1].Length + C_Spacing;
        }
    }

    public void AddDatasAtTop(List<MessageBaseData> datas)
    {
        if (datas.Count == 0) return;

        //Debug.LogError("Before!!!!");
        //for (int i = 0; i < mDatas.Count; i++)
        //{
        //    Debug.LogErrorFormat("ID:{0}  PosY:{1} Length:{2}", mDatas[i].ID, mDatas[i].PosY, mDatas[i].Length);
        //}

        List<MessageData> temps = new List<MessageData>();
        temps.Add(new MessageData(0, datas[0], 0));
        for (int i = 1; i < datas.Count; i++)
        {
            if (MinSize == 0) MinSize = datas[i].Length;
            else if (MinSize > datas[i].Length) MinSize = datas[i].Length;

            temps.Add(new MessageData(i, datas[i], temps[i - 1].PosY + temps[i - 1].Length + C_Spacing));
        }

        Datas.InsertRange(0, temps);
        for (int i = temps.Count; i < Datas.Count; i++)
        {
            Datas[i].PosY = Datas[i - 1].PosY + Datas[i - 1].Length + C_Spacing;
            Datas[i].ID += temps.Count;
        }

        //Debug.LogError("After!!!!");
        //for (int i = 0; i < mDatas.Count; i++)
        //{
        //    Debug.LogErrorFormat("ID:{0}  PosY:{1} Length:{2}", mDatas[i].ID, mDatas[i].PosY, mDatas[i].Length);
        //}
    }
}

public class MessageBaseData
{
    public int Length;
    public string Text;

    public MessageBaseData(int length, string text)
    {
        Length = length;
        Text = text;
    }
}

public class MessageData
{
    public float PosY;
    public float Length;
    public int ID;
    public string Text;

    public bool IsSelf;

    public MessageData(int index, MessageBaseData data, float posY)
    {
        ID = index;
        Length = data.Length;
        PosY = posY;
        Text = data.Text;

        IsSelf = UnityEngine.Random.Range(0, 5) > 1;
    }
}